#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <memcheck.h>
#include <stdlib.h>

//#include <poll_thread.h>
#include "poller.h"
#include "log.h"

CPollerObject::CPollerObject (CPollerUnit *o, int fd) :
    netfd (fd),
    ownerUnit(o),
    newEvents (0),
    oldEvents (0),
    epslot(NULL)
{
}

CPollerObject::~CPollerObject ()
{
	DetachPoller();

	if (ownerUnit && epslot)
		ownerUnit->FreeEpollSlot (epslot);

	if (netfd > 0)
    {
		log_debug ("netfd[%d] closed", netfd);
		close (netfd); 
		netfd = 0;
	}
    else
    {
        log_debug ("netfd[%d], already closed.", netfd);
    }
}

int CPollerObject::AttachPoller (CPollerUnit *unit)
{
	if(unit)
    {
		if( ownerUnit==NULL)
		{
			ownerUnit = unit;
		}
		else if (unit == ownerUnit){
			//reconnect .
		}
		else
			return -1;
	}
		
	if(netfd < 0)
		return -1;
	
	if(epslot==NULL)
    {
		if (!(epslot = ownerUnit->AllocEpollSlot ()))
        {
			return -1;
        }

		epslot->poller = this;

		int flag = fcntl (netfd, F_GETFL);
		fcntl (netfd, F_SETFL, O_NONBLOCK | flag);
		struct epoll_event ev;
		ev.events = newEvents;
		ev.data.u64 = (++epslot->seq);
		ev.data.u64 = (ev.data.u64 << 32) + ownerUnit->GetSlotId (epslot);

		if (ownerUnit->Epctl (EPOLL_CTL_ADD, netfd, &ev) == 0)
        {
			//printf("EPOLL ADD fd[%d] ev[%d] epslot[%d][%d]\n",netfd,ev.events,ownerUnit->GetSlotId (epslot),epslot->seq);
			oldEvents = newEvents;
        }
		else
        {
			return -1;
		}	
		return 0;

	}
	else
		return ApplyEvents ();
}

uint64_t CPollerObject::GetPollerEventId()
{
	if(epslot == NULL)
	{
		return 0;
	}
	uint64_t id = epslot->seq;
	return ((id << 32) + ownerUnit->GetSlotId (epslot));
}

int CPollerObject::DetachPoller() 
{
	if(epslot) 
	{
		struct epoll_event ev;
		if (ownerUnit->Epctl(EPOLL_CTL_DEL, netfd, &ev) == 0)
		{
			oldEvents = newEvents;
		}
		else 
		{
			//return -1;
		}
		ownerUnit->FreeEpollSlot(epslot);
		epslot = NULL;
	}
	return 0;
}

int CPollerObject::ApplyEvents ()
{
	if (epslot == NULL || oldEvents == newEvents)
		return 0;

	struct epoll_event ev;

	ev.events = newEvents;
	ev.data.u64 = (++epslot->seq);
	ev.data.u64 = (ev.data.u64 << 32) + ownerUnit->GetSlotId (epslot);
	if (ownerUnit->Epctl (EPOLL_CTL_MOD, netfd, &ev) == 0)
	{
		//printf("EPOLL MOD fd[%d] epslot[%d][%d]\n",netfd,ownerUnit->GetSlotId (epslot),epslot->seq);
		oldEvents = newEvents;
	}
	else
	{
		//log_warning("Epctl: %m");
		return -1;
	}

	return 0;
}

int CPollerObject::InputNotify(void)
{
	EnableInput(false);

    return POLLER_SUCC;
}

int CPollerObject::OutputNotify(void)
{
	EnableOutput(false);

    return POLLER_SUCC;
}

int CPollerObject::HangupNotify(void) 
{
    abort ();
	delete this;

    return POLLER_FAIL;
}

CPollerUnit::CPollerUnit(int mp)
{
	maxPollers   = mp;
	epfd         = -1;
	ep_events    = NULL;
	pollerTable  = NULL;
	freeSlotList = NULL;
	usedPollers  = 0;
}

CPollerUnit::~CPollerUnit() 
{
	for (int i = 0; i < maxPollers; i++) 
	{
		if (pollerTable[i].freeList)
			continue;
		//delete pollerTable[i].poller;
	}

	FREE_CLEAR(pollerTable);

	if (epfd != -1)
	{
		close (epfd);
		epfd = -1;
	}

	FREE_CLEAR(ep_events);
}

int CPollerUnit::SetMaxPollers(int mp)
{
	if(epfd >= 0)
    {
		return -1;
    }

	maxPollers = mp;

	return 0;
}

int CPollerUnit::InitializePollerUnit(void)
{
	pollerTable = (struct CEpollSlot *)CALLOC(maxPollers, sizeof (*pollerTable));

	if (!pollerTable)
	{
		log_error("calloc failed, num=%d, %m", maxPollers);
		return -1;
	}

	for (int i = 0; i < maxPollers - 1; i++)
	{
		pollerTable[i].freeList = &pollerTable[i+1];
	}

	pollerTable[maxPollers - 1].freeList = NULL;
	freeSlotList = &pollerTable[0];

	ep_events = (struct epoll_event *)CALLOC(maxPollers, sizeof (struct epoll_event));

	if (!ep_events)
	{
		log_error("malloc failed, %m");
		return -1;
	}

	if ((epfd = epoll_create (maxPollers)) == -1)
	{
		log_warning("epoll_create failed, %m");
		return -1;
	}
	return 0;
}

inline int CPollerUnit::VerifyEvents (struct epoll_event *ev)
{
	int idx = EPOLL_DATA_SLOT (ev);
	
	if ((idx >= maxPollers) || (EPOLL_DATA_SEQ (ev) != pollerTable[idx].seq))
	{
		return -1;
	}

	return 0;
}

void CPollerUnit::FreeEpollSlot (CEpollSlot *p)
{
	p->freeList = freeSlotList;
	p->poller = NULL;
	freeSlotList = p;
	usedPollers--;
	p->seq++;
}

struct CEpollSlot *CPollerUnit::AllocEpollSlot ()
{
	CEpollSlot *p = freeSlotList;

	if (0 == p) 
	{
		log_warning("no free epoll slot, usedPollers=%d", usedPollers);
		return NULL;
	}
	
	usedPollers++;
	freeSlotList = freeSlotList->freeList;
	p->freeList  = NULL;

	return p;
}

int CPollerUnit::Epctl (int op, int fd, struct epoll_event *events)
{
	if (epoll_ctl (epfd,  op, fd, events) == -1)
    {
		//log_warning("epoll_ctl error, epfd=%d, fd=%d", epfd, fd);

		return -1;
	}

	return 0;
}

void CPollerUnit::WaitPollerEvents(int timeout)
{
	nrEvents = epoll_wait (epfd, ep_events, maxPollers, timeout);
}

void CPollerUnit::ProcessPollerEvents(void)
{
    int ret_code = -1;
	
 	//log_debug ("events [%d]", nrEvents);
	for (int i = 0; i < nrEvents; i++)
	{
		if(VerifyEvents (ep_events+i) == -1)
		{
			log_notice("VerifyEvents failed, ep_events[%d].data.u64 = %llu", i, (unsigned long long)ep_events[i].data.u64);
			continue;
		}

		CEpollSlot *s = &pollerTable[EPOLL_DATA_SLOT(ep_events+i)];
		CPollerObject *p = s->poller;

		p->newEvents = p->oldEvents;

        if(ep_events[i].events & (EPOLLHUP | EPOLLERR))
		{
            log_debug ("EPOLLHUP | EPOLLERR netfd[%d]", p->netfd);
			p->HangupNotify();
			continue;
		}

		if(ep_events[i].events & EPOLLIN)
        {
            //log_debug ("EPOLLIN netfd[%d]", p->netfd);
			ret_code = p->InputNotify();

            //因为在InputNotify里面已经删除了自己，所以这里必须判断是否已经detach
            if (s->poller != p)
            {
                continue;
            }

            //再次检测返回值，如果不等于POLLER_SUCC，则表示已经delete掉自己，或者异常
            if (ret_code != POLLER_SUCC)
            {
                continue;
            }
        }

        if(ep_events[i].events & EPOLLOUT)
        {
            log_debug ("EPOLLOUT netfd[%d]", p->netfd);
			ret_code = p->OutputNotify();

            //因为在OutputNotify里面已经删除了自己，所以这里必须判断是否已经detach
            if (s->poller != p)
            {
                continue;
            }

            //再次检测返回值，如果不等于POLLER_SUCC，则表示已经delete掉自己，或者异常
            if (ret_code != POLLER_SUCC)
            {
                continue;
            }
        }

        //因为在InputNotify, OutputNotify退出后，已经检测指针是否已经detach，因此这里无需判断
   		//if(s->poller==p)
        //{
			p->ApplyEvents();
        //}
	}
}

